#include <stdio.h>
#include <iostream>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "mem_share.h"

using namespace std;
#define FILEPATH "shared_memory_file"
#define BOXSIZE (sizeof(Addrbox) * 1024)
#define FILESIZE (maxnum * BOXSIZE)

static int maxnum = 8;
static int get_mm_fd()
{
    int fd = open(FILEPATH, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        perror("open failed");
    }
    return fd;
}

static int open_shared_mm(int *fd, void **addr)
{
    *fd = get_mm_fd();
    if (*fd == -1) {
        return -1;
    }

    if (ftruncate(*fd, FILESIZE) == -1) {
        perror("ftruncate failed");
        close(*fd);
        return -1;
    }
    *addr = (void *)mmap(NULL, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
    if (*addr == MAP_FAILED) {
        perror("mmap failed");
        close(*fd);
        return -1;
    }
    return 0;
}

int shared_mm_init(int num)
{
    Addrbox *addrboxs;
    int fd;

    maxnum = num;
    if (open_shared_mm(&fd, (void **)(&addrboxs)) == -1) {
        perror("open shared mem failed");
        return -1;
    }

    memset(addrboxs, 0, FILESIZE);
	if (msync(addrboxs, FILESIZE, MS_SYNC) == -1) {
        perror("sync failed");
    }
    munmap(addrboxs, FILESIZE);
    close(fd);
    return 0;
}

int shared_mm_write(int id, void *addr, size_t size)
{
    Addrbox *addrboxs;
    int fd;
    if (id >= maxnum) {
        perror("out of range!");
        return -1;
    }
    if (open_shared_mm(&fd, (void **)(&addrboxs)) == -1) {
        perror("open shared mem failed");
        return -1;
    }

    addrboxs[id].ready = true;
    addrboxs[id].addr = addr;
    addrboxs[id].size = size;
    if (msync(addrboxs, FILESIZE, MS_SYNC) == -1) {
        perror("sync failed");
    }
    munmap(addrboxs, FILESIZE);
    close(fd);
    return 0;
}

int shared_mm_read(int id, void **addr, size_t *size)
{
    Addrbox *addrboxs;
    int fd;
    *addr = NULL;
    *size = 0;
    if (id >= maxnum) {
        perror("out of range!");
        return -1;
    }

    if (open_shared_mm(&fd, (void **)(&addrboxs)) == -1) {
        perror("open shared mem failed");
        return -1;
    }    

    if (addrboxs[id].ready) {
	*addr = addrboxs[id].addr;
	*size = addrboxs[id].size;
    } else {
        //perror("shared data not ready!");
        munmap(addrboxs, FILESIZE);
        close(fd);
        return -1;
    }
    munmap(addrboxs, FILESIZE);
    close(fd);
    return 0;
}
